﻿using Microsoft.Kinect;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DetectingKinectSencor.RecordStream
{
    /// <summary>
    /// Parts of this code has been used from http://channel9.msdn.com/Series/KinectSDKQuickstarts/Audio-Fundamentals
    /// Parts of this code has been used from the book: Beginning kinect programming with the microsoft kinect sdk
    /// </summary>
    class RecordAudioStream
    {
        private bool DEBUGon = MainWindow.DEBUGrecordAudioStream;



        public bool reading { get; set; }

        private Stream audioFileStream;
        private byte[] audioBuffer;
        private Thread readingThread;
        UnicodeEncoding uniEncoding = new UnicodeEncoding();



        /// <summary>
        /// Number of milliseconds between each read of audio data from the stream.
        /// </summary>
        private const int AudioPollingInterval = 50;

        /// <summary>
        /// Number of samples captured from Kinect audio stream each millisecond.
        /// </summary>
        private const int SamplesPerMillisecond = 16;

        /// <summary>
        /// Number of bytes in each Kinect audio stream sample.
        /// </summary>
        private const int BytesPerSample = 2;

        //Size of the audio file
        private int size;
        private KinectAudioSource audioSource;
        private Stream audioSourceStream;


        /// <summary>
        /// Constructor for Record Audio stream. Starts the audio listen thread
        /// </summary>
        /// <param name="audioFileStream"></param>
        /// <param name="audioSource"></param>
        /// <param name="recTime"></param>
        public RecordAudioStream(Stream audioFileStream, KinectAudioSource audioSource)
        {
            size = 0;

            //Buffer for the audio stream
            audioBuffer = new byte[AudioPollingInterval * SamplesPerMillisecond * BytesPerSample];

            // Save the audio source stream
            this.audioSource = audioSource;

            // Save the stream to which the audio is written
            this.audioFileStream = audioFileStream;


            // Set noise suppresion to true
            audioSource.NoiseSuppression = true;

            //Start the audio source do detect audio
            audioSourceStream= audioSource.Start();

            // Write the audio header
            writeWavHeader(audioFileStream, size);

            // Start the background thread
            this.readingThread = new Thread(AudioReadingThread);
            this.readingThread.Start();

        }

       
        private void AudioReadingThread()
        {
            while (this.reading)
            {
                int readCount = audioSourceStream.Read(audioBuffer, 0, audioBuffer.Length);

                audioFileStream.Write(audioBuffer, 0, readCount);
                size += audioBuffer.Length;
            }

            // Save the current position in the stream
            long prePosition = audioFileStream.Position;

            // Go to the beginning of the stream where the header is located
            audioFileStream.Seek(0, SeekOrigin.Begin);

            // Write real wav header, because now the real size is known
            writeWavHeader(audioFileStream, size);

            // Go back to the previous saved position
            audioFileStream.Seek(prePosition, SeekOrigin.Begin);
            audioFileStream.Flush();

        }

        private void stopAudioRecord()
        {
            this.reading = false;

            if (readingThread != null)
            {
                readingThread.Join();
            }

        }

        public void stopAudioRecording()
        {
            stopAudioRecord();
        }

        /// <summary>
        /// A bare bones WAV file header writer
        /// </summary>        
        static void writeWavHeader(Stream stream, int dataLength)
        {
            //We need to use a memory stream because the BinaryWriter will close the underlying stream when it is closed
            using (var memStream = new MemoryStream(64))
            {
                int cbFormat = 18; //sizeof(WAVEFORMATEX)
                WAVEFORMATEX format = new WAVEFORMATEX()
                {
                    wFormatTag = 1,
                    nChannels = 1,
                    nSamplesPerSec = 16000,
                    nAvgBytesPerSec = 32000,
                    nBlockAlign = 2,
                    wBitsPerSample = 16,
                    cbSize = 0
                };

                using (var bw = new BinaryWriter(memStream))
                {
                    //RIFF header
                    WriteString(memStream, "RIFF");
                    bw.Write(dataLength + cbFormat + 4); //File size - 8
                    WriteString(memStream, "WAVE");
                    WriteString(memStream, "fmt ");
                    bw.Write(cbFormat);

                    //WAVEFORMATEX
                    bw.Write(format.wFormatTag);
                    bw.Write(format.nChannels);
                    bw.Write(format.nSamplesPerSec);
                    bw.Write(format.nAvgBytesPerSec);
                    bw.Write(format.nBlockAlign);
                    bw.Write(format.wBitsPerSample);
                    bw.Write(format.cbSize);

                    //data header
                    WriteString(memStream, "data");
                    bw.Write(dataLength);
                    memStream.WriteTo(stream);
                }
            }
        }

        static void WriteString(Stream stream, string s)
        {
            byte[] bytes = Encoding.ASCII.GetBytes(s);
            stream.Write(bytes, 0, bytes.Length);
        }
    }
} 
    
    
